#include "ShaderGroup.h"


ShaderGroup::ShaderGroup()
{
	m_pVertexShader = 0;
	m_pPixelShader = 0;
	m_pInputLayout = 0;
	m_pVSConstantBuffer = 0;
	m_pGeometryShader = 0;
	m_pGSConstantBuffer = 0;
	m_pPSConstantBuffer[0] = 0;
	m_pPSConstantBuffer[1] = 0;
	m_pPSConstantBuffer[2] = 0;


	m_pSampleState = 0;
}


ShaderGroup::~ShaderGroup()
{
}

HRESULT ShaderGroup::InitializeShaders(ID3D11Device* device, const void* PixelShaderByteCode, UINT psByteCount, const void* VertexShaderByteCode, UINT vsByteCount, const void* GeometryShaderByteCode, UINT gsByteCount)
{

	HRESULT hr;
	D3D11_INPUT_ELEMENT_DESC vLayout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "UVS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	// TODO: PART 2 STEP 8b
	hr = device->CreateInputLayout(vLayout, 4, VertexShaderByteCode, vsByteCount, &m_pInputLayout);
	if (hr != S_OK)
		return hr;

	D3D11_SAMPLER_DESC samplerDesc;
	ZeroMemory(&samplerDesc, sizeof(samplerDesc));
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDesc.MipLODBias = 0.0f;
	samplerDesc.MaxAnisotropy = 1;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	// Create the texture sampler state.
	hr = device->CreateSamplerState(&samplerDesc, &m_pSampleState);
	if (hr != S_OK)
		return hr;

	VS_CONSTANT_BUFFER constBuffer;
	D3D11_BUFFER_DESC wmConstBuffDesc;
	ZeroMemory(&wmConstBuffDesc, sizeof(wmConstBuffDesc));
	wmConstBuffDesc.Usage = D3D11_USAGE_DYNAMIC;
	wmConstBuffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	wmConstBuffDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	wmConstBuffDesc.ByteWidth = sizeof(VS_CONSTANT_BUFFER);
	wmConstBuffDesc.StructureByteStride = 0;

	D3D11_SUBRESOURCE_DATA wmSubSource;
	ZeroMemory(&wmSubSource, sizeof(wmSubSource));
	wmSubSource.pSysMem = &constBuffer;

	hr = device->CreateBuffer(&wmConstBuffDesc, &wmSubSource, &m_pVSConstantBuffer);
	if (hr != S_OK)
		return hr;
	
	if (GeometryShaderByteCode)
	{
		hr = device->CreateBuffer(&wmConstBuffDesc, &wmSubSource, &m_pGSConstantBuffer);
		if (hr != S_OK)
			return hr;

		hr = device->CreateGeometryShader(GeometryShaderByteCode, gsByteCount, NULL, &m_pGeometryShader);
		if (hr != S_OK)
			return hr;
	}
	
	DIR_LIGHT_BUFFER dlConstBuffer;
	
	ZeroMemory(&wmConstBuffDesc, sizeof(wmConstBuffDesc));
	wmConstBuffDesc.Usage = D3D11_USAGE_DYNAMIC;
	wmConstBuffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	wmConstBuffDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	wmConstBuffDesc.ByteWidth = sizeof(DIR_LIGHT_BUFFER);
	wmConstBuffDesc.StructureByteStride = 0;

	
	ZeroMemory(&wmSubSource, sizeof(wmSubSource));
	wmSubSource.pSysMem = &dlConstBuffer;

	hr = device->CreateBuffer(&wmConstBuffDesc, &wmSubSource, &m_pPSConstantBuffer[0]);
	if (hr != S_OK)
		return hr;

	POINT_LIGHT_BUFFER plConstBuffer;
	ZeroMemory(&wmConstBuffDesc, sizeof(wmConstBuffDesc));
	wmConstBuffDesc.Usage = D3D11_USAGE_DYNAMIC;
	wmConstBuffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	wmConstBuffDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	wmConstBuffDesc.ByteWidth = sizeof(POINT_LIGHT_BUFFER);
	wmConstBuffDesc.StructureByteStride = 0;


	ZeroMemory(&wmSubSource, sizeof(wmSubSource));
	wmSubSource.pSysMem = &plConstBuffer;

	hr = device->CreateBuffer(&wmConstBuffDesc, &wmSubSource, &m_pPSConstantBuffer[1]);
	if (hr != S_OK)
		return hr;

	SPOT_LIGHT_BUFFER slConstBuffer;
	ZeroMemory(&wmConstBuffDesc, sizeof(wmConstBuffDesc));
	wmConstBuffDesc.Usage = D3D11_USAGE_DYNAMIC;
	wmConstBuffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	wmConstBuffDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	wmConstBuffDesc.ByteWidth = sizeof(SPOT_LIGHT_BUFFER);
	wmConstBuffDesc.StructureByteStride = 0;


	ZeroMemory(&wmSubSource, sizeof(wmSubSource));
	wmSubSource.pSysMem = &slConstBuffer;

	hr = device->CreateBuffer(&wmConstBuffDesc, &wmSubSource, &m_pPSConstantBuffer[2]);
	if (hr != S_OK)
		return hr;


	

	hr = device->CreateVertexShader(VertexShaderByteCode, vsByteCount, NULL, &m_pVertexShader);
	if (hr != S_OK)
		return hr;
	hr = device->CreatePixelShader(PixelShaderByteCode, psByteCount, NULL, &m_pPixelShader);

	
	
	return hr;
}
void ShaderGroup::ShutDown()
{
	if (m_pVertexShader)
	{
		m_pVertexShader->Release();
		m_pVertexShader = 0;
	}
	if (m_pPixelShader)
	{
		m_pPixelShader->Release();
		m_pPixelShader = 0;
	}
	if (m_pGeometryShader)
	{
		m_pGeometryShader->Release();
		m_pGeometryShader = 0;
	}
	if (m_pGSConstantBuffer)
	{
		m_pGSConstantBuffer->Release();
		m_pGSConstantBuffer = 0;
	}
	
	if (m_pInputLayout)
	{
		m_pInputLayout->Release();
		m_pInputLayout = 0;
	}
	if (m_pVSConstantBuffer)
	{
		m_pVSConstantBuffer->Release();
		m_pVSConstantBuffer = 0;
	}
	if (m_pPSConstantBuffer[0])
	{
		m_pPSConstantBuffer[0]->Release();
		m_pPSConstantBuffer[0] = 0;
	}
	if (m_pPSConstantBuffer[1])
	{
		m_pPSConstantBuffer[1]->Release();
		m_pPSConstantBuffer[1] = 0;
	}
	if (m_pPSConstantBuffer[2])
	{
		m_pPSConstantBuffer[2]->Release();
		m_pPSConstantBuffer[2] = 0;
	}
	if (m_pSampleState)
	{
		m_pSampleState->Release();
		m_pSampleState = 0;
	}

}
HRESULT ShaderGroup::SetVShaderInfo(ID3D11DeviceContext* pContext, VS_CONSTANT_BUFFER* vsbufferInfo)
{
	HRESULT hr;
	D3D11_MAPPED_SUBRESOURCE subRsrc;
	ZeroMemory(&subRsrc, sizeof(subRsrc));
	hr = pContext->Map(m_pVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subRsrc);
	if (hr != S_OK)
		return hr;
	
	memcpy(subRsrc.pData, vsbufferInfo, sizeof(VS_CONSTANT_BUFFER));
	
	pContext->Unmap(m_pVSConstantBuffer, 0);


	

	pContext->VSSetConstantBuffers(0, 1, &m_pVSConstantBuffer);

	
	return hr;
}
HRESULT ShaderGroup::SetGShaderInfo(ID3D11DeviceContext* pContext, VS_CONSTANT_BUFFER* gsbufferInfo)
{
	HRESULT hr = S_OK;
	if (!m_pGeometryShader)
	{
		return hr;
	}
	D3D11_MAPPED_SUBRESOURCE subRsrc;
	ZeroMemory(&subRsrc, sizeof(subRsrc));
	hr = pContext->Map(m_pGSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subRsrc);
	if (hr != S_OK)
		return hr;

	memcpy(subRsrc.pData, gsbufferInfo, sizeof(VS_CONSTANT_BUFFER));

	pContext->Unmap(m_pGSConstantBuffer, 0);




	pContext->GSSetConstantBuffers(0, 1, &m_pGSConstantBuffer);


	return hr;
}
HRESULT ShaderGroup::SetPShaderInfo(ID3D11DeviceContext* pContext, ID3D11ShaderResourceView* textureResource, DIR_LIGHT_BUFFER* dlbufferInfo, POINT_LIGHT_BUFFER* plbufferInfo, SPOT_LIGHT_BUFFER* slbufferInfo)
{
	HRESULT hr;
	D3D11_MAPPED_SUBRESOURCE subRsrc;
	ZeroMemory(&subRsrc, sizeof(subRsrc));
	hr = pContext->Map(m_pPSConstantBuffer[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &subRsrc);
	if (hr != S_OK)
		return hr;

	memcpy(subRsrc.pData, dlbufferInfo, sizeof(DIR_LIGHT_BUFFER));
	
	pContext->Unmap(m_pPSConstantBuffer[0], 0);


	ZeroMemory(&subRsrc, sizeof(subRsrc));
	hr = pContext->Map(m_pPSConstantBuffer[1], 0, D3D11_MAP_WRITE_DISCARD, 0, &subRsrc);
	if (hr != S_OK)
		return hr;

	memcpy(subRsrc.pData, plbufferInfo, sizeof(POINT_LIGHT_BUFFER));

	pContext->Unmap(m_pPSConstantBuffer[1], 0);


	ZeroMemory(&subRsrc, sizeof(subRsrc));
	hr = pContext->Map(m_pPSConstantBuffer[2], 0, D3D11_MAP_WRITE_DISCARD, 0, &subRsrc);
	if (hr != S_OK)
		return hr;

	memcpy(subRsrc.pData, slbufferInfo, sizeof(SPOT_LIGHT_BUFFER));

	pContext->Unmap(m_pPSConstantBuffer[2], 0);


	pContext->PSSetConstantBuffers(0, 3, m_pPSConstantBuffer);
	pContext->PSSetShaderResources(0, 1, &textureResource);
	return hr;
}
void ShaderGroup::AddShadersToPipeline(ID3D11DeviceContext* pContext)
{
	pContext->IASetInputLayout(m_pInputLayout);
	pContext->VSSetShader(m_pVertexShader, NULL, 0);
	pContext->PSSetShader(m_pPixelShader, NULL, 0);

	if (m_pGeometryShader)
	{
		pContext->GSSetShader(m_pGeometryShader, NULL, 0);
		pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);

	}
	else
	{
		pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	}
	pContext->PSSetSamplers(0, 1, &m_pSampleState);


}